﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;

namespace TiaoTiaoCode.Common.ClassEntities
{
    public class TiaoTiaoFTP : IDisposable
    {
        #region 字段

        /// <summary>
        /// Ftp请求对象
        /// </summary>
        private FtpWebRequest? _request = null;

        /// <summary>
        /// Ftp响应对象
        /// </summary>
        private FtpWebResponse? _response = null;

        ///// <summary>
        ///// 是否需要删除临时文件
        ///// </summary>
        //private bool _isDeleteTempFile = false;

        ///// <summary>
        ///// 异步上传时临时生成的文件
        ///// </summary>
        //private string _uploadTempFile = string.Empty;

        #endregion

        #region 属性

        ///<summary>
        /// Ftp服务器地址:wifi.ttoocc.com/
        /// </summary>
        public string ServerUri { get; set; } = string.Empty;

        /// <summary>
        /// Ftp服务器登录用户
        /// </summary>
        public string UserName { get; set; } = string.Empty;

        /// <summary>
        /// Ftp服务器登录密码
        /// </summary>
        public string Password { get; set; } = string.Empty;

        #endregion

        #region 构造函数

        /// <summary>
        /// 
        /// </summary>
        public TiaoTiaoFTP() { }

        /// <summary>
        /// 实例化FTP帮助类
        /// </summary>
        /// <param name="serverUri">FTP服务器地址（IP或域名）</param>
        /// <param name="userName">FTP用户名</param>
        /// <param name="password">FTP密码</param>
        public TiaoTiaoFTP(string serverUri, string userName, string password)
        {
            ServerUri = serverUri;
            UserName = userName;
            Password = password;
        }

        #endregion

        #region 实现接口

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            if (_request != null)
            {
                _request.Abort();
                _request = null;
            }
            if (_response != null)
            {
                _response.Close();
                _response = null;
            }
        }

        #endregion


        #region 事件

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="fileLocalPath">需要上传的文件</param>
        /// <param name="remoteDir">目标路径</param>
        /// <param name="newFileName">新的文件名</param>
        public bool UploadFile(string fileLocalPath, string remoteDir, string newFileName)
        {
            FileInfo file = new FileInfo(fileLocalPath);
            if (remoteDir.Trim() == "") return false;

            string URI = "FTP://" + ServerUri + "/" + remoteDir + "/" + newFileName;
            _request = null;
            if (!FtpIsExistsDir(remoteDir))
                CreateDir(remoteDir);

            if (FtpIsExistsFile(remoteDir + "/" + newFileName))
            {
                _request = GetRequest(URI);
                _request.KeepAlive = false;
                _request.Method = System.Net.WebRequestMethods.Ftp.DeleteFile; //删除
                _request.GetResponse();
            }

            //URI = "FTP://" + hostname + "/" + targetDir + "/" + newFileName;
            _request = null;
            _request = GetRequest(URI);
            _request.UseBinary = true;
            _request.UsePassive = true;
            _request.KeepAlive = false;
            _request.Method = System.Net.WebRequestMethods.Ftp.UploadFile;

            //指定文件传输的数据类型
            //告诉ftp文件大小
            _request.ContentLength = file.Length;
            //缓冲大小设置为2KB
            int BufferSize = 2048, lastBufferSize, count;
            //计算包个数
            count = (int)file.Length / BufferSize + 1;
            //计算最后一个包大小
            lastBufferSize = (int)file.Length % BufferSize;
            byte[] content = new byte[BufferSize];
            int dataRead;
            //打开一个文件流 (System.IO.FileStream) 去读上传的文件
            using (FileStream fs = file.OpenRead())
            {
                try
                {
                    //把上传的文件写入流
                    using (Stream rs = _request.GetRequestStream())
                    {
                        while (count > 1)
                        {
                            dataRead = fs.Read(content, 0, BufferSize);
                            rs.Write(content, 0, BufferSize);
                            count--;
                        }
                        content = new byte[lastBufferSize];
                        dataRead = fs.Read(content, 0, lastBufferSize);
                        rs.Write(content, 0, lastBufferSize);
                        rs.Close();
                        rs.Dispose();
                        fs.Close();
                        fs.Dispose();
                        return true;
                    }
                }
                catch (Exception)
                {
                    return false;
                }
                finally
                {
                    fs.Close();
                    fs.Dispose();
                }
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="localDir">下载至本地路径</param>
        /// <param name="ftpDir">ftp目标文件路径</param>
        /// <param name="ftpFile">从ftp要下载的文件名</param>
        public void DownloadFile(string localDir, string ftpDir, string ftpFile)
        {
            string URI = "FTP://" + ServerUri + "/" + ftpDir + "/" + ftpFile;
            string tmpname = Guid.NewGuid().ToString();
            string localfile = localDir + @"\" + tmpname;
            if (!Directory.Exists(localDir))
                Directory.CreateDirectory(localDir);
            _request = GetRequest(URI);
            _request.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
            _request.UseBinary = true;
            _request.UsePassive = false;
            using (FtpWebResponse response = (FtpWebResponse)_request.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (FileStream fs = new FileStream(localfile, FileMode.CreateNew))
                    {
                        try
                        {
                            byte[] buffer = new byte[2048];
                            int read = 0;
                            do
                            {
                                read = responseStream.Read(buffer, 0, buffer.Length);
                                fs.Write(buffer, 0, read);
                            } while (read != 0);
                            responseStream.Close();
                            fs.Flush();
                            fs.Close();
                        }
                        catch (Exception)
                        {
                            fs.Close();
                            File.Delete(localfile);
                            throw;
                        }
                    }
                    responseStream.Close();
                }
                response.Close();
            }

            try
            {
                File.Delete(localDir + @"\" + ftpFile);
                File.Move(localfile, localDir + @"\" + ftpFile);
            }
            catch (Exception ex)
            {
                File.Delete(localfile);
                throw ex;
            }
            _request = null;
        }

        /// <summary>
        /// 从FTP上下载文件夹及其内容（递归）
        /// </summary>
        /// <param name="localDir"></param>
        /// <param name="ftpDir"></param>
        /// <returns></returns>
        public bool DownloadDirectory(string localDir, string ftpDir)
        {
            var list = GetDirectoriesListInfo(ftpDir);
            foreach (var item in list)
            {
                if (item.Groups[1].Value == "d")
                {
                    DownloadDirectory(localDir + @"\" + item.Groups[6].Value, ftpDir + "/" + item.Groups[6].Value);
                }
                else
                {
                    var file = new FileInfo(localDir + @"\" + item.Groups[6].Value);
                    if (file.Exists)
                    {
                        var ftpDateTime = GetLastModified(ftpDir + @"\" + item.Groups[6].Value);
                        var localDataTime = file.LastWriteTime;
                        if (ftpDateTime <= localDataTime)//若本地文件比FTP文件新，则不用下载该文件
                            continue;
                    }
                    DownloadFile(localDir, ftpDir, item.Groups[6].Value);
                }
            }
            return true;
        }

        /// <summary>
        /// 搜索远程文件
        /// </summary>
        /// <param name="targetDir">远程文件夹(不能以"/"结尾) e.q. "aaa/bbb/ccc"</param>
        /// <returns></returns>
        public List<string> GetDirectoriesList(string targetDir)
        {
            var lastDirName = targetDir.Substring(targetDir.LastIndexOf('/') + 1);
            List<string> result = new List<string>();
            try
            {
                string URI = "FTP://" + ServerUri + "/" + targetDir;
                _request = GetRequest(URI);
                _request.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;
                _request.UsePassive = true;
                _request.UseBinary = true;


                string str = GetStringResponse(_request);
                str = str.Replace(lastDirName + "/", null);
                if (str != string.Empty)
                    result.AddRange(str.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
                return result;
            }
            catch
            {
                return default!;
            }
        }

        /// <summary>
        /// 搜索远程文件夹
        /// </summary>
        /// <param name="targetDir"></param>
        /// <param name="searchKey"></param>
        /// <returns></returns>
        public List<string> GetDirectoriesList(string targetDir, string searchKey)
        {
            var lastDirName = targetDir.Substring(targetDir.LastIndexOf('/') + 1);
            List<string> result = new List<string>();
            try
            {
                string URI = "FTP://" + ServerUri + "/" + targetDir;
                _request = GetRequest(URI);
                _request.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;
                _request.UsePassive = true;
                _request.UseBinary = true;


                string str = GetStringResponse(_request);
                str = str.Replace(lastDirName + "/", null);
                if (str != string.Empty)
                    result.AddRange(str.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
                return result.Where(m => m.Contains(searchKey)).ToList();
            }
            catch
            {
                return default!;
            }
        }

        /// <summary>
        /// 获取FTP指定目录下文件及文件夹列表
        /// </summary>
        /// <param name="targetDir"></param>
        /// <returns></returns>
        public List<Match> GetDirectoriesListInfo(string targetDir)
        {
            var lastDirName = targetDir.Substring(targetDir.LastIndexOf('/') + 1);
            List<Match> result = new List<Match>();
            try
            {
                string URI = "FTP://" + ServerUri + "/" + targetDir;
                _request = GetRequest(URI);
                _request.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails;
                _request.UsePassive = true;
                _request.UseBinary = true;


                string str = GetStringResponse(_request);
                List<string> strs = new List<string>();
                if (str != string.Empty)
                    strs.AddRange(str.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries));
                Regex regex = new Regex(@"^([d-])([rwxt-]{3}){3}\s+\d{1,}\s+.*?(\d{1,})\s+(\w+\s+\d{1,2}\s+(?:\d{4})?)(\d{1,2}:\d{2})?\s+(.+?)\s?$", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
                foreach (var item in strs)
                {
                    Match match = regex.Match(item);
                    result.Add(match);
                }
                return result;
            }
            catch
            {
                return default!;
            }
        }

        /// <summary>
        /// 获取文件或文件夹的最后修改时间
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        private DateTime GetLastModified(string path)
        {
            try
            {
                string uri = "ftp://" + ServerUri + "/" + path;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
                _response = (FtpWebResponse)_request.GetResponse();
                return _response.LastModified;
            }
            catch (Exception)
            {
                return DateTime.MinValue;
            }
        }

        /// <summary>
        /// 在ftp服务器上创建目录
        /// </summary>
        /// <param name="dirName">目录名称</param>
        /// <returns></returns>
        public bool CreateDir(string dirName)
        {
            try
            {
                string uri = "ftp://" + ServerUri + "/" + dirName;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.MakeDirectory;
                _response = (FtpWebResponse)_request.GetResponse();
                _response.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="dirName">删除的目录名称</param>
        public bool DelDir(string dirName)
        {
            try
            {
                string uri = "ftp://" + ServerUri + "/" + dirName;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.RemoveDirectory;
                _response = (FtpWebResponse)_request.GetResponse();
                _response.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 文件重命名
        /// </summary>
        /// <param name="currentFilename">当前目录名称</param>
        /// <param name="newFilename">重命名目录名称</param>
        public bool Rename(string currentFilename, string newFilename)
        {
            try
            {
                FileInfo fileInf = new FileInfo(currentFilename);
                string uri = "ftp://" + ServerUri + "/" + fileInf.Name;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.Rename;
                _request.RenameTo = newFilename;
                _response = (FtpWebResponse)_request.GetResponse();
                _response.Close();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 判断ftp服务器上该目录是否存在
        /// </summary>
        /// <param name="dirName"></param>
        /// <param name="ftpHostIP"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool FtpIsExistsDir(string dirName)
        {
            bool flag = true;
            try
            {
                string uri = "ftp://" + ServerUri + "/" + dirName;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.ListDirectory;
                _response = (FtpWebResponse)_request.GetResponse();
                _response.Close();
            }
            catch (Exception)
            {
                flag = false;
            }
            return flag;
        }

        /// <summary>
        /// 检测文件是否存在
        /// </summary>
        /// <param name="fileFullName"></param>
        /// <param name="ftpHostIP"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool FtpIsExistsFile(string fileFullName)
        {
            bool flag = true;
            try
            {
                string uri = "ftp://" + ServerUri + "/" + fileFullName;
                _request = GetRequest(uri);
                _request.Method = WebRequestMethods.Ftp.GetFileSize;
                _response = (FtpWebResponse)_request.GetResponse();
                _response.Close();
            }
            catch (Exception)
            {
                flag = false;
            }
            return flag;
        }

        private FtpWebRequest GetRequest(string URI)
        {
            //根据服务器信息FtpWebRequest创建类的对象
            FtpWebRequest result = (FtpWebRequest)FtpWebRequest.Create(URI);
            //提供身份验证信息
            result.Credentials = new System.Net.NetworkCredential(UserName, Password);
            //设置请求完成之后是否保持到FTP服务器的控制连接，默认值为true
            result.KeepAlive = false;
            return result;
        }

        private string GetStringResponse(FtpWebRequest ftp)
        {
            //Get the result, streaming to a string
            string result = "";
            using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
            {
                long size = response.ContentLength;
                using (Stream datastream = response.GetResponseStream())
                {
                    using (StreamReader sr = new StreamReader(datastream, System.Text.Encoding.UTF8))
                    {
                        result = sr.ReadToEnd();
                        sr.Close();
                    }
                    datastream.Close();
                }
                response.Close();
            }
            return result;
        }

        #endregion
    }
}
